
# 导入 weakref 模块，用于创建弱引用，此处存在未使用问题
import weakref
# 从 uuid 模块导入 UUID 和 uuid4，其中 UUID 存在未使用问题
from uuid import UUID, uuid4

from qtpy import QtCore
from qtpy import QtGui
from qtpy.QtWidgets import QGraphicsPathItem
from qtpy.QtWidgets import QGraphicsEllipseItem
from qtpy.QtWidgets import QMenu
from qtpy.QtWidgets import QStyle

from src.UI.Utils.stylesheet import editableStyleSheet, Colors, ConnectionTypes
from src.UI.Canvas.UICommon import NodeDefaults
from src.UI.Canvas.Painters import ConnectionPainter
from src.Core.Common import *


# UIConnection between pins
class UIConnection(QGraphicsPathItem):
    """
    UIConnection 是一个三次样条曲线，代表两个引脚之间的连接。

    :param source: 连接的源引脚
    :param destination: 连接的目标引脚
    :param canvas: 包含此连接的画布对象
    """

    def __init__(self, source, destination, canvas):
        # 调用父类 QGraphicsPathItem 的构造函数
        QGraphicsPathItem.__init__(self)
        # 设置该对象接受的鼠标按钮为左键，此处存在类 'Qt' 的未解析的特性引用 'LeftButton' 问题
        self.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
        # 启用鼠标悬停事件
        self.setAcceptHoverEvents(True)
        # 设置该对象可被选择，此处存在类 'QGraphicsPathItem' 的未解析的特性引用 'ItemIsSelectable' 问题
        self.setFlag(QGraphicsPathItem.ItemIsSelectable)
        # 创建一个右键菜单
        self._menu = QMenu()
        # 为菜单添加一个 "Disconnect" 动作
        self.actionDisconnect = self._menu.addAction("Disconnect")
        # 连接动作的触发信号到 kill 方法
        self.actionDisconnect.triggered.connect(self.kill)
        # 生成一个唯一的标识符
        self._uid = uuid4()
        # 创建对画布的弱引用
        self.canvasRef = weakref.ref(canvas)
        # 创建对源引脚的弱引用
        self.source = weakref.ref(source)
        # 创建对目标引脚的弱引用
        self.destination = weakref.ref(destination)
        # 获取源引脚对象
        self.drawSource = self.source()
        # 获取目标引脚对象
        self.drawDestination = self.destination()

        # 用于覆盖获取端点位置的方法
        # 如果为 None，则使用引脚中心位置
        self.sourcePositionOverride = None
        self.destinationPositionOverride = None

        # 创建一个绘制路径对象
        self.mPath = QtGui.QPainterPath()

        # 定义两个控制点
        self.cp1 = QtCore.QPointF(0.0, 0.0)
        self.cp2 = QtCore.QPointF(0.0, 0.0)

        # 设置该对象的 Z 值，使其位于节点之下
        self.setZValue(NodeDefaults().Z_LAYER - 1)

        # 获取源引脚的颜色
        self.color = self.source().color()
        # 计算选中时的颜色
        self.selectedColor = self.color.lighter(150)

        # 初始化线条粗细
        self.thickness = 1
        # 线条粗细的乘数
        self.thicknessMultiplier = 1
        # 如果源引脚是执行引脚，则增加线条粗细
        if source.isExec():
            self.thickness = 2

        # 创建一个画笔对象，此处存在类 'Qt' 的未解析的特性引用 'SolidLine'、'RoundCap'、'RoundJoin' 问题
        self.pen = QtGui.QPen(
            self.color,
            self.thickness,
            QtCore.Qt.SolidLine,
            QtCore.Qt.RoundCap,
            QtCore.Qt.RoundJoin,
        )

        # 获取连接的端点位置
        points = self.getEndPoints()
        # 根据端点位置更新曲线
        self.updateCurve(points[0], points[1])

        # 设置画笔
        self.setPen(self.pen)

        # 更新源引脚和目标引脚的显示
        self.source().update()
        self.destination().update()
        # 初始化淡入淡出值
        self.fade = 0.0
        # 将此连接添加到源引脚和目标引脚的连接列表中
        self.source().uiConnectionList.append(self)
        self.destination().uiConnectionList.append(self)
        # 通知源引脚和目标引脚已连接
        self.source().pinConnected(self.destination())
        self.destination().pinConnected(self.source())
        # 记录上一次的位置
        self.prevPos = None
        # 初始化线性路径
        self.linPath = None
        # 初始化各种偏移量
        self.hOffsetL = 0.0
        self.hOffsetR = 0.0
        self.hOffsetLSShape = 0.0
        self.hOffsetRSShape = 0.0
        self.vOffset = 0.0
        self.vOffsetSShape = 0.0
        self.offsetting = 0
        # 标记是否垂直对齐到第一个端点
        self.snapVToFirst = True
        # 标记是否垂直对齐到第二个端点
        self.snapVToSecond = False
        # 标记是否为 S 形曲线
        self.sShape = False
        # 标记悬停的线段索引
        self.hoverSegment = -1
        # 标记按下的线段索引
        self.pressedSegment = -1
        # 如果源引脚是执行引脚，创建一个气泡对象
        if self.source().isExec():
            self.bubble = QGraphicsEllipseItem(-2.5, -2.5, 5, 5, self)
            self.bubble.setBrush(self.color)
            self.bubble.setPen(self.pen)

            # 获取路径上的起始点
            point = self.mPath.pointAtPercent(0.0)
            # 设置气泡的位置
            self.bubble.setPos(point)

            # 隐藏气泡
            self.bubble.hide()

            # 连接源引脚的执行信号到 performEvaluationFeedback 方法
            self.source().OnPinExecute.connect(self.performEvaluationFeedback)
            # 标记是否需要动画
            self.shouldAnimate = False
            # 创建一个时间线对象
            self.timeline = QtCore.QTimeLine(2000)
            # 设置时间线的帧范围
            self.timeline.setFrameRange(0, 100)
            # 连接时间线的帧改变信号到 timelineFrameChanged 方法
            self.timeline.frameChanged.connect(self.timelineFrameChanged)
            # 设置时间线循环次数为无限
            self.timeline.setLoopCount(0)

    def performEvaluationFeedback(self, *args, **kwargs):
        """
        执行评估反馈动画，当源引脚执行时触发。
        如果时间线未运行，则显示气泡并启动时间线。
        """
        if self.timeline.state() == QtCore.QTimeLine.State.NotRunning:
            self.shouldAnimate = True
            # 显示气泡
            self.bubble.show()
            # 启动时间线
            self.timeline.start()

    def timelineFrameChanged(self, frameNum):
        """
        时间线帧改变时的处理方法，更新气泡的位置。
        如果不需要动画，则停止时间线并隐藏气泡。

        :param frameNum: 当前的帧编号
        """
        # 计算执行时间百分比
        percentage = (
                currentProcessorTime() - self.source()._rawPin.getLastExecutionTime()
        )
        self.shouldAnimate = percentage < 0.5
        # 根据帧编号获取路径上的点
        point = self.mPath.pointAtPercent(
            float(frameNum) / float(self.timeline.endFrame())
        )
        # 设置气泡的位置
        self.bubble.setPos(point)
        if not self.shouldAnimate:
            # 停止时间线
            self.timeline.stop()
            # 隐藏气泡
            self.bubble.hide()

    def setSelected(self, value):
        """
        设置该连接是否被选中，调用父类的方法。

        :param value: True 表示选中，False 表示未选中
        """
        super(UIConnection, self).setSelected(value)

    def isUnderCollapsedComment(self):
        """
        判断该连接是否在折叠的注释节点之下。
        如果源节点和目标节点在同一个折叠的注释节点之下，则返回 True。

        :return: 如果在折叠的注释节点之下返回 True，否则返回 False
        """
        srcNode = self.source().owningNode()
        dstNode = self.destination().owningNode()
        srcComment = srcNode.owningCommentNode
        dstComment = dstNode.owningCommentNode
        if (
                srcComment is not None
                and dstComment is not None
                and srcComment == dstComment
                and srcComment.collapsed
        ):
            return True
        return False

    def isUnderActiveGraph(self):
        """
        判断该连接是否在当前活动的图形中。

        :return: 如果在当前活动的图形中返回 True，否则返回 False
        """
        return (
                self.canvasRef().graphManager.activeGraph()
                == self.source()._rawPin.owningNode().graph()
        )

    def __repr__(self):
        """
        返回该连接的字符串表示形式，显示源引脚和目标引脚的全名。

        :return: 连接的字符串表示形式
        """
        return "{0} -> {1}".format(
            self.source().getFullName(), self.destination().getFullName()
        )

    def setColor(self, color):
        """
        设置连接的颜色，更新画笔的颜色。

        :param color: 要设置的颜色
        """
        self.pen.setColor(color)
        self.color = color

    def updateEndpointsPositions(self):
        """
        更新连接的端点位置，根据注释节点的状态覆盖端点位置。
        """
        srcNode = self.source().owningNode()
        dstNode = self.destination().owningNode()

        srcComment = srcNode.owningCommentNode
        if srcComment is not None:
            # 如果源节点有注释节点，检查注释节点是否折叠或在其他折叠注释节点之下
            srcNodeUnderCollapsedComment = srcComment.isUnderCollapsedComment()
            topMostCollapsedComment = srcNode.getTopMostOwningCollapsedComment()
            if srcComment.collapsed:
                # 如果注释节点折叠，覆盖源端点位置
                rightSideEndpointGetter = srcComment.getRightSideEdgesPoint
                if srcNodeUnderCollapsedComment:
                    rightSideEndpointGetter = (
                        topMostCollapsedComment.getRightSideEdgesPoint
                    )
                self.sourcePositionOverride = rightSideEndpointGetter
            else:
                if srcNodeUnderCollapsedComment:
                    # 如果源节点在其他折叠注释节点之下，覆盖源端点位置
                    self.sourcePositionOverride = (
                        topMostCollapsedComment.getRightSideEdgesPoint
                    )
                else:
                    # 否则，不覆盖源端点位置
                    self.sourcePositionOverride = None
        else:
            # 如果源节点没有注释节点，不覆盖源端点位置
            self.sourcePositionOverride = None

        # 对目标节点进行相同的处理
        dstComment = dstNode.owningCommentNode
        if dstComment is not None:
            dstNodeUnderCollapsedComment = dstComment.isUnderCollapsedComment()
            topMostCollapsedComment = dstNode.getTopMostOwningCollapsedComment()
            if dstComment.collapsed:
                rightSideEndpointGetter = dstComment.getLeftSideEdgesPoint
                if dstNodeUnderCollapsedComment:
                    rightSideEndpointGetter = (
                        topMostCollapsedComment.getLeftSideEdgesPoint
                    )
                self.destinationPositionOverride = rightSideEndpointGetter
            else:
                if dstNodeUnderCollapsedComment:
                    self.destinationPositionOverride = (
                        topMostCollapsedComment.getLeftSideEdgesPoint
                    )
                else:
                    self.destinationPositionOverride = None
        else:
            self.destinationPositionOverride = None

    def Tick(self):
        """
        每帧更新该连接的状态。
        检查连接是否仍然有效，如果无效则移除连接。
        根据连接的状态更新画笔的颜色。
        """
        # 检查该实例是否代表有效的连接，如果不是则销毁
        if not arePinsConnected(self.source()._rawPin, self.destination()._rawPin):
            self.canvasRef().removeConnection(self)

        if self.drawSource.isExec() or self.drawDestination.isExec():
            if self.thickness != 2:
                self.thickness = 2
                self.pen.setWidthF(self.thickness)

        if self.isSelected():
            self.pen.setColor(self.selectedColor)
        else:
            self.pen.setColor(self.color)
        self.update()

    def contextMenuEvent(self, event):
        """
        处理右键菜单事件，显示右键菜单。

        :param event: 右键菜单事件对象
        """
        self._menu.exec_(event.screenPos())

    @property
    def uid(self):
        """
        获取该连接的唯一标识符。

        :return: 连接的唯一标识符
        """
        return self._uid

    @uid.setter
    def uid(self, value):
        """
        设置该连接的唯一标识符，并更新画布中的连接字典。

        :param value: 要设置的唯一标识符
        """
        if self._uid in self.canvasRef().connections:
            self.canvasRef().connections[value] = self.canvasRef().connections.pop(
                self._uid
            )
            self._uid = value

    # TODO: Check why this serialization sometimes fails
    def applyJsonData(self, data):
        """
        从 JSON 数据中应用连接的偏移量和对齐信息。

        :param data: 包含连接信息的 JSON 数据
        """
        hOffsetL = data["hOffsetL"]
        if hOffsetL is not None:
            self.hOffsetL = float(hOffsetL)
        hOffsetR = data["hOffsetR"]
        if hOffsetR is not None:
            self.hOffsetR = float(hOffsetR)
        hOffsetLSShape = data["hOffsetLSShape"]
        if hOffsetLSShape is not None:
            self.hOffsetLSShape = float(hOffsetLSShape)
        hOffsetRSShape = data["hOffsetRSShape"]
        if hOffsetRSShape is not None:
            self.hOffsetRSShape = float(hOffsetRSShape)
        vOffset = data["vOffset"]
        if vOffset is not None:
            self.vOffset = float(vOffset)
        vOffsetSShape = data["vOffsetSShape"]
        if vOffsetSShape is not None:
            self.vOffsetSShape = float(vOffsetSShape)
        snapVToFirst = data["snapVToFirst"]
        if snapVToFirst is not None:
            self.snapVToFirst = bool(snapVToFirst)
        snapVToSecond = data["snapVToSecond"]
        if snapVToSecond is not None:
            self.snapVToSecond = bool(snapVToSecond)

        self.getEndPoints()

    def serialize(self):
        """
        将该连接的信息序列化为字典。

        :return: 包含连接信息的字典
        """
        script = {
            "sourceUUID": str(self.source().uid),
            "destinationUUID": str(self.destination().uid),
            "sourceName": self.source()._rawPin.getFullName(),
            "destinationName": self.destination()._rawPin.getFullName(),
            "uuid": str(self.uid),
            "hOffsetL": str(self.hOffsetL),
            "hOffsetR": str(self.hOffsetR),
            "hOffsetLSShape": str(self.hOffsetLSShape),
            "hOffsetRSShape": str(self.hOffsetRSShape),
            "vOffset": str(self.vOffset),
            "vOffsetSShape": str(self.vOffsetSShape),
            "snapVToFirst": int(self.snapVToFirst),
            "snapVToSecond": int(self.snapVToSecond),
        }

        return script

    def __str__(self):
        """
        返回该连接的字符串表示形式，显示源引脚和目标引脚的全名。

        :return: 连接的字符串表示形式
        """
        return "{0} >>> {1}".format(
            self.source()._rawPin.getFullName(),
            self.destination()._rawPin.getFullName(),
        )

    def drawThick(self):
        """
        绘制较粗的连接线条，并改变线条颜色。
        """
        self.pen.setWidthF(self.thickness + (self.thickness / 1.5))
        f = 0.5
        r = abs(lerp(self.color.red(), Colors.Yellow.red(), clamp(f, 0, 1)))
        g = abs(lerp(self.color.green(), Colors.Yellow.green(), clamp(f, 0, 1)))
        b = abs(lerp(self.color.blue(), Colors.Yellow.blue(), clamp(f, 0, 1)))
        self.pen.setColor(QtGui.QColor.fromRgb(r, g, b))

    def restoreThick(self):
        """
        恢复连接线条的原始粗细和颜色。
        """
        self.pen.setWidthF(self.thickness)
        self.pen.setColor(self.color)

    def hoverEnterEvent(self, event):
        """
        处理鼠标悬停进入事件，绘制较粗的线条并更新显示。

        :param event: 鼠标悬停进入事件对象
        """
        super(UIConnection, self).hoverEnterEvent(event)
        self.drawThick()
        self.update()

    def hoverLeaveEvent(self, event):
        """
        处理鼠标悬停离开事件，恢复线条的原始状态并更新显示。

        :param event: 鼠标悬停离开事件对象
        """
        super(UIConnection, self).hoverLeaveEvent(event)
        self.hoverSegment = -1
        self.restoreThick()
        self.update()

    def hoverMoveEvent(self, event):
        """
        处理鼠标悬停移动事件，判断悬停的线段。

        :param event: 鼠标悬停移动事件对象
        """
        if self.offsetting == 0:
            self.hoverSegment = -1
            if self.linPath is not None:
                # 创建线性路径
                tempPath = ConnectionPainter.linearPath(self.linPath)
                # 计算鼠标位置在线性路径上的百分比
                t = self.percentageByPoint(event.scenePos(), tempPath)
                segments = []
                for i, pos in enumerate(self.linPath[:-1]):
                    # 计算每个线段的起始和结束百分比
                    t1 = self.percentageByPoint(pos, tempPath)
                    t2 = self.percentageByPoint(self.linPath[i + 1], tempPath)
                    segments.append([t1, t2])
                for i, seg in enumerate(segments):
                    if t > seg[0] and t < seg[1]:
                        valid = []
                        if not self.sShape:
                            if self.snapVToFirst:
                                valid = [0, 1]
                            elif self.snapVToSecond:
                                valid = [1, 2]
                            else:
                                valid = [1, 2, 3]
                        else:
                            valid = [1, 2, 3]
                        if i in valid:
                            self.hoverSegment = i
                        else:
                            self.hoverSegment = -1

    def getEndPoints(self):
        """
        获取连接的端点位置，根据覆盖位置进行调整。

        :return: 源端点和目标端点的位置元组
        """
        # 获取源引脚的场景位置加上引脚中心位置
        p1 = self.drawSource.scenePos() + self.drawSource.pinCenter()
        if self.sourcePositionOverride is not None:
            # 如果有覆盖位置，使用覆盖位置
            p1 = self.sourcePositionOverride()

        # 获取目标引脚的场景位置加上引脚中心位置
        p2 = self.drawDestination.scenePos() + self.drawDestination.pinCenter()
        if self.destinationPositionOverride is not None:
            # 如果有覆盖位置，使用覆盖位置
            p2 = self.destinationPositionOverride()

        if editableStyleSheet().ConnectionMode[0] in [
            ConnectionTypes.Circuit,
            ConnectionTypes.ComplexCircuit,
        ]:
            # 如果连接模式为 Circuit 或 ComplexCircuit，处理特殊情况
            self.sameSide = 0
            p1n, p2n = p1, p2
            xDistance = p2.x() - p1.x()
            if self.destination().owningNode()._rawNode.__class__.__name__ in [
                "reroute",
                "rerouteExecs",
            ]:
                if xDistance < 0:
                    p2n, p1n = p1, p2
                    self.sameSide = 1
            if self.source().owningNode()._rawNode.__class__.__name__ in [
                "reroute",
                "rerouteExecs",
            ]:
                if xDistance < 0:
                    p1n, p2n = p1, p2
                    self.sameSide = -1
            p1, p2 = p1n, p2n
        return p1, p2

    def percentageByPoint(self, point, path, precision=0.5, width=20.0):
        """
        计算指定点在路径上的百分比位置。

        :param point: 要计算的点
        :param path: 路径对象
        :param precision: 计算精度，默认为 0.5
        :param width: 路径的宽度，默认为 20.0
        :return: 点在路径上的百分比位置，如果未找到则返回 -1.0
        """
        percentage = -1.0
        # 创建一个画笔路径描边器
        stroker = QtGui.QPainterPathStroker()
        stroker.setWidth(width)
        # 创建路径的描边路径
        strokepath = stroker.createStroke(path)
        t = 0.0
        d = []
        while t <= 100.0:
            # 计算点到路径上每个点的距离
            d.append(QtGui.QVector2D(point - path.pointAtPercent(t / 100)).length())
            t += precision
        # 找到最小距离的索引并计算百分比
        percentage = d.index(min(d)) * precision
        return percentage

    def mousePressEvent(self, event):
        """
        处理鼠标按下事件，记录相关信息并判断偏移类型。

        :param event: 鼠标按下事件对象
        """
        super(UIConnection, self).mousePressEvent(event)
        # 计算鼠标位置在路径上的百分比
        t = self.percentageByPoint(event.scenePos(), self.mPath)
        # 记录鼠标按下的位置
        self.prevPos = event.pos()

        if abs(self.mPath.slopeAtPercent(t * 0.01)) < 1:
            # 如果路径在该点的斜率小于 1，设置偏移类型为水平偏移
            self.offsetting = 1
        else:
            # 否则，设置偏移类型为垂直偏移
            self.offsetting = 2

        if self.linPath is not None:
            # 如果有线性路径，判断按下的线段
            tempPath = ConnectionPainter.linearPath(self.linPath)
            t = self.percentageByPoint(event.scenePos(), tempPath)
            segments = []
            for i, pos in enumerate(self.linPath[:-1]):
                t1 = self.percentageByPoint(pos, tempPath)
                t2 = self.percentageByPoint(self.linPath[i + 1], tempPath)
                segments.append([t1, t2])
            for i, seg in enumerate(segments):
                if t > seg[0] and t < seg[1]:
                    valid = []
                    if not self.sShape:
                        if self.snapVToFirst:
                            valid = [0, 1]
                        elif self.snapVToSecond:
                            valid = [1, 2]
                        else:
                            valid = [1, 2, 3]
                    else:
                        valid = [1, 2, 3]
                    if i in valid:
                        self.pressedSegment = i
                    else:
                        self.pressedSegment = -1
        # 获取连接的端点位置
        p1, p2 = self.getEndPoints()
        offset1 = editableStyleSheet().ConnectionOffset[0]
        offset2 = -offset1
        if self.sameSide == 1:
            offset2 = offset1
        elif self.sameSide == -1:
            offset1 = offset2
        xDistance = (p2.x() + offset2) - (p1.x() + offset1)
        self.sShape = xDistance < 0
        event.accept()

    def mouseReleaseEvent(self, event):
        """
        处理鼠标释放事件，重置偏移类型和按下的线段索引。

        :param event: 鼠标释放事件对象
        """
        super(UIConnection, self).mouseReleaseEvent(event)
        self.offsetting = 0
        self.pressedSegment = -1

        event.accept()

    def mouseMoveEvent(self, event):
        """
        处理鼠标移动事件，根据偏移类型和按下的线段更新偏移量。

        :param event: 鼠标移动事件对象
        """
        super(UIConnection, self).mouseMoveEvent(event)
        if self.prevPos is not None:
            # 计算鼠标移动的偏移量
            delta = self.prevPos - event.pos()
            # 获取连接的端点位置
            p1, p2 = self.getEndPoints()
            if not self.sShape:
                if self.offsetting == 1:
                    doIt = True
                    if self.snapVToFirst and self.pressedSegment != 0:
                        doIt = False
                        self.pressedSegment = -1
                    elif self.snapVToSecond and self.pressedSegment != 2:
                        doIt = False
                        self.pressedSegment = -1
                    elif not self.snapVToFirst and not self.snapVToSecond:
                        if self.pressedSegment != 2:
                            doIt = False
                            self.pressedSegment = -1
                    if doIt:
                        # 更新垂直偏移量
                        self.vOffset -= float(delta.y())
                        if abs(self.vOffset) <= 3:
                            self.snapVToFirst = True
                            self.pressedSegment = 0
                        else:
                            self.snapVToFirst = False
                        if p2.y() - 3 < p1.y() + self.vOffset < p2.y() + 3:
                            self.snapVToSecond = True
                            self.pressedSegment = 2
                        else:
                            self.snapVToSecond = False
                        if not self.snapVToFirst and self.pressedSegment == 0:
                            self.pressedSegment = 2

                if self.offsetting == 2:
                    if self.snapVToFirst:
                        # 更新右侧水平偏移量
                        self.hOffsetR -= float(delta.x())
                    elif self.snapVToSecond:
                        # 更新左侧水平偏移量
                        self.hOffsetL -= float(delta.x())
                    else:
                        if self.pressedSegment == 1:
                            self.hOffsetL -= float(delta.x())
                        elif self.pressedSegment == 3:
                            self.hOffsetR -= float(delta.x())
            else:
                if self.offsetting == 1 and self.pressedSegment == 2:
                    # 更新 S 形曲线的垂直偏移量
                    self.vOffsetSShape -= float(delta.y())
                elif self.offsetting == 2:
                    if self.pressedSegment == 1:
                        # 更新 S 形曲线的右侧水平偏移量
                        self.hOffsetRSShape -= float(delta.x())
                    elif self.pressedSegment == 3:
                        # 更新 S 形曲线的左侧水平偏移量
                        self.hOffsetLSShape -= float(delta.x())

            # 记录当前鼠标位置
            self.prevPos = event.pos()

        event.accept()

    def source_port_name(self):
        """
        获取源引脚的全名。

        :return: 源引脚的全名
        """
        return self.source().getFullName()

    def shape(self):
        """
        获取连接的形状，用于碰撞检测等。

        :return: 连接的形状路径对象
        """
        qp = QtGui.QPainterPathStroker()
        qp.setWidth(10.0)
        # 设置画笔的端点样式，此处存在类 'Qt' 的未解析的特性引用 'SquareCap' 问题
        qp.setCapStyle(QtCore.Qt.SquareCap)
        return qp.createStroke(self.path())

    def updateCurve(self, p1, p2):
        """
        根据端点位置更新连接的曲线。

        :param p1: 源端点位置
        :param p2: 目标端点位置
        """
        # 计算端点之间的 x 轴距离
        xDistance = p2.x() - p1.x()
        multiply = 3
        # 创建一个新的绘制路径
        self.mPath = QtGui.QPainterPath()

        # 移动到源端点位置
        self.mPath.moveTo(p1)
        if xDistance < 0:
            # 如果目标端点在源端点左侧，绘制三次贝塞尔曲线
            self.mPath.cubicTo(
                QtCore.QPoint(p1.x() + xDistance / -multiply, p1.y()),
                QtCore.QPoint(p2.x() - xDistance / -multiply, p2.y()),
                p2,
            )
        else:
            # 否则，绘制另一种三次贝塞尔曲线
            self.mPath.cubicTo(
                QtCore.QPoint(p1.x() + xDistance / multiply, p1.y()),
                QtCore.QPoint(p2.x() - xDistance / 2, p2.y()),
                p2,
            )

        # 设置连接的路径
        self.setPath(self.mPath)

    def kill(self):
        """
        从画布中移除该连接。
        """
        self.canvasRef().removeConnection(self)

    def paint(self, painter, option, widget):
        """
        绘制连接，此处存在方法签名与基类不匹配问题，
        且存在类 'QStyleOptionGraphicsItem' 的未解析的特性引用 'state'
        和类 'QStyle' 的未解析的特性引用 'State_Selected' 问题。

        :param painter: 用于绘制的 QPainter 对象
        :param option: 绘制选项
        :param widget: 可选的绘制部件
        """
        # 清除选择状态
        option.state &= ~QStyle.State_Selected

        # 获取画布的细节层次值
        lod = self.canvasRef().getCanvasLodValueFromCurrentScale()

        # 设置画笔
        self.setPen(self.pen)
        # 获取连接的端点位置
        p1, p2 = self.getEndPoints()
        # 获取连接的圆角半径
        roundness = editableStyleSheet().ConnectionRoundness[0]
        # 获取连接的偏移量
        offset = editableStyleSheet().ConnectionOffset[0]
        offset1 = offset
        offset2 = -offset1
        if self.sameSide == 1:
            offset2 = offset1
        elif self.sameSide == -1:
            offset1 = offset2
        # 计算端点之间的 x 轴距离
        xDistance = (p2.x() + offset2) - (p1.x() + offset1)
        self.sShape = xDistance < 0
        sectionPath = None
        if editableStyleSheet().ConnectionMode[0] == ConnectionTypes.Circuit:
            # 如果连接模式为 Circuit，调用相应的绘制方法
            seg = (
                self.hoverSegment
                if self.hoverSegment != -1
                   and self.linPath
                   and self.pressedSegment == -1
                else self.pressedSegment
            )
            self.mPath, self.linPath, sectionPath = ConnectionPainter.BasicCircuit(
                p1,
                p2,
                offset,
                roundness,
                self.sameSide,
                lod,
                False,
                self.vOffset,
                self.hOffsetL,
                self.vOffsetSShape,
                self.hOffsetR,
                self.hOffsetRSShape,
                self.hOffsetLSShape,
                self.snapVToFirst,
                self.snapVToSecond,
                seg,
            )
        elif editableStyleSheet().ConnectionMode[0] == ConnectionTypes.ComplexCircuit:
            # 如果连接模式为 ComplexCircuit，调用相应的绘制方法
            self.mPath, self.linPath, sectionPath = ConnectionPainter.BasicCircuit(
                p1, p2, offset, roundness, self.sameSide, lod, True
            )
        elif editableStyleSheet().ConnectionMode[0] == ConnectionTypes.Cubic:
            # 如果连接模式为 Cubic，调用相应的绘制方法
            self.mPath = ConnectionPainter.Cubic(p1, p2, 150, lod)
            self.linPath = None
        elif editableStyleSheet().ConnectionMode[0] == ConnectionTypes.Linear:
            # 如果连接模式为 Linear，调用相应的绘制方法
            self.mPath, _ = ConnectionPainter.Linear(p1, p2, offset, roundness, lod)
            self.linPath = None
        if self.snapVToSecond and self.offsetting == 0:
            # 如果垂直对齐到第二个端点且没有偏移，更新垂直偏移量
            self.vOffset = p2.y() - p1.y()
        # 设置连接的路径
        self.setPath(self.mPath)

        # 调用父类的绘制方法
        super(UIConnection, self).paint(painter, option, widget)
        # 创建一个新的画笔
        pen = QtGui.QPen()
        pen.setColor(editableStyleSheet().MainColor)
        pen.setWidthF(self.thickness + (self.thickness / 1.5))
        painter.setPen(pen)
        if sectionPath:
            # 如果有分段路径，绘制分段路径
            painter.drawPath(sectionPath)